$MOD186
$EP
Name RTOS_TASKS

;---------------------------------------------------------------------------
; RTOS_TASKS related functions for 8088 microcomputer system
;
; Filename:	RTOS_TASKS.ASM
;
; Author: 	Phang Tian He
;
; This file contains proprietory information and cannot be copied 
; or distributed without prior permission from the author.
;---------------------------------------------------------------------------
;API:
public TASK_INIT			; Called at the beginning of the program to ensure the RTOS is initialized probably.
public REGISTER_TASK		; Called by the main function to register other task to the RTOS
public SURRENDER_CONTROL  	; Called by the periodic task to pass the control to next available task when it finished its functions.
public SCHEDULER			; a TIMER_ISR, which should be put into the interrupt vector table directly
							; -- pick the earliest deadline task for execution, Earliest Deadline Scheduling.
public CLOCK_AND_TRACKER	; a TIMER_ISR, which should be put into the interrupt vector table directly
							; -- keep track the global clock and wake up the sleeping periodic task if necessary.

; ************************************************ SYSTEM DEFINITIONS ********************************************
NUMBER_TASKS 	EQU 	5
SIZE_TASK_INFO	EQU 	2*5 

; IF THE CLOCK IS HIGHER THEN THESHOLD VALUE, REDUCE THE CLOCK AND ALL DEADLINE BY REDUCTION VALUE TO PREVENT OVERFLOW.
GLOBAL_CLOCK_OVERFLOW_PREVENTION_THESHOLD    	EQU 0C000H
GLOBAL_CLOCK_REDUCTION              			EQU 08000H 
; ****************************************************************************************************************

TASKS_DATA_SEGMENT 	SEGMENT
	GLOBAL_CLOCK    	DW  0BFF6H
	TASKS_CONTROL_INFO 	DB 	NUMBER_TASKS*SIZE_TASK_INFO         DUP (0)

	RUNNING_TASK    	DB CONST_MAIN_LOGIC_ID 
	READY_QUEUE     	DB 5 DUP(0)
	WAITING_QUEUE   	DB 5 DUP(0)

	NEXT_AVAILABLE_ID   DB  2
TASKS_DATA_SEGMENT	ENDS

; ********************************************** CONSTANT DEFINITIONS ********************************************                                            
TRUE    EQU 1
FALSE   EQU 0

CONST_MAIN_LOGIC_ID EQU 1

TASK_BASE       				EQU OFFSET  TASKS_CONTROL_INFO
OFFSET_TASK_ID_POSITION 		EQU 0+TASK_BASE
OFFSET_TASK_DEADLINE_POSITION   EQU 2+TASK_BASE
OFFSET_TASK_PERIOD_POSITION     EQU 4+TASK_BASE
OFFSET_TASK_SS_POSITION 		EQU 6+TASK_BASE
OFFSET_TASK_SP_POSITION 		EQU 8+TASK_BASE
; **************************************************************************************************************** 

TASK_FUNCTIONS 		SEGMENT
ASSUME CS:TASK_FUNCTIONS
;=====================================================================
;	FUNCTION NAME: TASK_INIT
;	INPUTS: 		-
;	OUTPUTS:		-
;	DESCRIPTION:	INITIALIZATION CODE FOR RTOS TASKS. WILL PREPARE 
; 					TASK ID IN THE TASK CONTROL SPACE. 
;
; 					****THE STACK MEMORY IS GIVEN BY THE TASK PROGRAMMER
; 					****DEFAULT TASK 1 IS ALWAYS THE MAIN LOGIC TASK
;	WHEN TO CALL:	ONCE AFTER SYSTEM WAS STARTED.
;=====================================================================
TASK_INIT PROC FAR
    PUSH    AX
    PUSH    BX
    PUSH    ES
    
    MOV     AX, TASKS_DATA_SEGMENT
    MOV     ES, AX
    
    ; INIT TASK
    MOV     AX, NUMBER_TASKS
    TASK_INIT_LOOP:
        CMP AX, 0000H
        JE  TASK_INIT_END_LOOP
        
        PUSH    AX        
        DEC     AX
        XOR     BX, BX
        MOV     BX, SIZE_TASK_INFO
        MUL     BX
        MOV     BX, AX        
        POP     AX    
        
        ; INIT TASK ID
        MOV WORD PTR ES:[BX+OFFSET_TASK_ID_POSITION], AX
        
        DEC AX
        JMP TASK_INIT_LOOP
    TASK_INIT_END_LOOP:
    
    POP     ES
    POP     BX
    POP     AX
    RET
TASK_INIT ENDP

;=====================================================================
;	FUNCTION NAME: GET_TASK_DEADLINE
;	INPUTS: 		AX  ----->   TASK ID 
;	OUTPUTS:		BX  ----->   TASK DEADLINE
;	DESCRIPTION:	RETRIEVE THE TASK'S DEADLINE INFORMATION BY 
; 					THE TASK'S ID.
;=====================================================================
GET_TASK_DEADLINE PROC FAR
    PUSH    AX
    PUSH    ES 
    
    MOV     BX, TASKS_DATA_SEGMENT
    MOV     ES, BX
    
    DEC AX
    MOV BL, SIZE_TASK_INFO
    MUL BL
    MOV BX, AX
    MOV BX, WORD PTR ES:[BX+OFFSET_TASK_DEADLINE_POSITION] 
    
    POP     ES
    POP     AX
    RET
GET_TASK_DEADLINE ENDP


;=====================================================================
;	FUNCTION NAME: 	ENQUEUE
;	INPUTS: 		ES  ----->   QUEUE_SEGMENT
; 					SI  ----->   QUEUE_OFFSET (FIRST LOCATION)
; 					DX  ----->   TASK ID   
;	OUTPUTS:		-
;	DESCRIPTION:	INSERT A TASK ID INTO A QUEUE, CAN BE WAITING OR READY QUEUE
; 	PSEUDO CODE: 	
		; WHILE( QUEUE[I]!=0 ) {
		;   CURRENT_TASK_ID = QUEUE[I];
		;
		;   CURRENT_TASK_DEADLINE = ES:TASK_BASE[CURRENT_TASK_ID-1)*8+2];
		;
		;   IF ( CURRENT_TASK_DEADLINE>ADDING_DEADLINE ){
		;       QUEUE[I] = ADDING_TASK_ID;
		;       ADDING_TASK_ID = CURRENT_TASK_ID;
		;       ADDING_DEADLINE = CURRENT_TASK_DEADLINE;
		;   } 
		;
		;   I++;              
		; } 
;=====================================================================
ENQUEUE PROC FAR
    PUSH    AX
    PUSH    BX
    PUSH    CX
    PUSH    DX
    
    PUSH    SI
    PUSH    ES
    
;****** DX = ADDING_TASK_ID; ******
    MOV 	AX, DX
    CALL 	FAR PTR GET_TASK_DEADLINE
    
;****** BX = ADDING_TASK_DEADLINE; ******
     
    
    NEXT_TASK_ID:
        XOR     AX, AX
        MOV     AL, BYTE PTR ES:[SI]
;****** AX = CURRENT_TASK_DEADLINE; ****** 

        CMP     AL, 00H
        JE      NO_MORE_INDEX
        
        ; TEST WHETHER IS INSERTING THE DEFAULT MAIN LOGIC TASK
        CMP     DX, CONST_MAIN_LOGIC_ID
        JE      NOT_EARLIER_DEADLINE
        
        ; THE MAIN LOGIC TASK, TASK ID =1, IS ALWAYS AT THE END OF THE QUEUE.
        CMP     AX, CONST_MAIN_LOGIC_ID
        JE      IS_EARLIER_DEADLINE


        PUSH    BX
        CALL 	FAR PTR GET_TASK_DEADLINE
        MOV     CX, BX        
        POP     BX        
 ;****** CX = CURRENT_TASK_DEADLINE; ******
        
        
        CMP     CX, BX
        JBE     NOT_EARLIER_DEADLINE
        IS_EARLIER_DEADLINE:
            MOV     BYTE PTR ES:[SI], DL
            MOV     DX, AX
            MOV     BX, CX
            
        NOT_EARLIER_DEADLINE:   
                
        INC     SI
        JMP     NEXT_TASK_ID
    
       
    NO_MORE_INDEX:    
    ; INSERT LAST NODE INTO THE LIST
    MOV     BYTE PTR ES:[SI], DL
    
    
    POP     ES
    POP     SI
    
    POP     DX
    POP     CX
    POP     BX
    POP     AX
    RET
ENQUEUE ENDP 

;=====================================================================
;	FUNCTION NAME: 	PEEK_QUEUE
;	INPUTS: 		ES  ----->   QUEUE_SEGMENT
; 					SI  ----->   QUEUE_OFFSET (FIRST LOCATION) 
; 
;	OUTPUTS:		AX  ----->   OUTPUT INDEX 
;	DESCRIPTION:	RETRIEVE THE FIRST TASK ID FROM THE QUEUE BUT NOT DELETING IT FROM QUEUE
;=====================================================================
PEEK_QUEUE PROC FAR
    XOR     AX, AX
    MOV     AL, BYTE PTR ES:[SI]
    ; AX = FIRST INDEX (RESULT)
    RET
PEEK_QUEUE ENDP

;=====================================================================
;	FUNCTION NAME: 	DEQUEUE
;	INPUTS: 		ES  ----->   QUEUE_SEGMENT
; 					SI  ----->   QUEUE_OFFSET (FIRST LOCATION)  
;	OUTPUTS:		AX  ----->   OUTPUT INDEX 
;	DESCRIPTION:	RETRIEVE THE FIRST TASK ID FROM THE QUEUE AND DELETE IT FROM QUEUE
;=====================================================================
DEQUEUE PROC FAR
    PUSH    BX
    PUSH    SI
    
    XOR     AX, AX
    MOV     AL, BYTE PTR ES:[SI]
    ; AX = FIRST INDEX (RESULT)
    
    SWITCH_BY_ONE_PLACE:
    MOV     BL, BYTE PTR ES:[SI+1]
    
    MOV     BYTE PTR ES:[SI], BL
    INC     SI
    CMP     BL, 00H
    JNE     SWITCH_BY_ONE_PLACE
    
    POP     SI
    POP     BX
    RET
DEQUEUE ENDP 

;=====================================================================
;	FUNCTION NAME: 	REGISTER_TASK
;	INPUTS: 		ES  ----->   STACK SEGMENT
; 					SI  ----->   TOP OF STACK 
; 					DS  ----->   TASK CS 
; 					DI  ----->   TASK IP 
; 					DX  ----->   PERIOD  
;	OUTPUTS:		DX  -----> 'TRUE'=SUCCESS, 'FALSE'=FAIL 
;	DESCRIPTION:	REGISTER A NEVER ENDED PROCEDURE AS A TASK TO RTOS
; 					**** TASK PROGRAMMER NEEDED TO PREPARE ITS OWN STACK MEMORY
;							BASED ON THE AMOUNT OF MEMORY NEEDED FOR THAT TASK.
; 					**** NOTES: RTOS MIGHT USE UPTO 40 BYTES OF STACK MEMORY.
; 					
;					**** THIS FUNCTION SHOULD ONLY BE CALLED BY THE DEFAULT 
;						MAIN FUNCTION, WHICH HAVE THE TASK ID=1.
;					**** THIS FUNCTION SHOULD ONLY BE CALLED AT THE VERY
;						BEGINNING OF THE SYSTEM.
;					
;					
;=====================================================================
REGISTER_TASK   PROC FAR
    PUSH    AX
    PUSH    BX
    PUSH    CX
    PUSH    DS
    PUSH 	DI
    PUSH 	ES
    PUSH 	SI

    ; ENTERING CRITICAL SECTION
    CLI
    
    PUSH    ES                          
	    MOV     AX, TASKS_DATA_SEGMENT
	    MOV     ES, AX    
	    XOR     AX, AX 
	    MOV     AL, BYTE PTR ES:NEXT_AVAILABLE_ID
    POP     ES
    
    ; IF THERE IS NOT MORE AVAILABLE TASK CONTROL SLOT, FAIL TO ALLOCATE
    CMP     AL, NUMBER_TASKS
    JA      FAIL_TO_REGISTER

	; AX = ASSIGNING TASK ID

    PUSH    AX
    PUSH    BX
    PUSH    CX
    	; **************** STACK MATCHING **************** 
    	; UPDATING REQUESTING TASK STACK TO ENSURE IT WILL RETURN 
    	; 	TO CORRECT CS:IP WHEN THE SCHEDULER/SURRENDER FUNCTIONS ARE CALLED.
	    MOV     BX, SS
	    MOV     CX, SP  

	    MOV     AX, ES
	    MOV     SS, AX
	    MOV     SP, SI 

	    XOR     AX, AX
	    PUSHF       ; PUSH THE FLAGS    
	    PUSH    DS  ; PUSH THE CS
	    PUSH    DI  ; PUSH THE IP
	    PUSH    AX
	    PUSH    AX
	    PUSH    AX
	    PUSH    AX
	    PUSH    AX
	    PUSH    AX
	    PUSH    AX
	    PUSH    AX
	    PUSH    AX    

	    MOV     AX, SS  
	    MOV     ES, AX
	    MOV     SI, SP    

	    MOV     SP, CX
	    MOV     SS, BX

    POP     CX
    POP     BX
    POP     AX

    ;  DS:DI IS ALREADY STORED IN STACK, FREE FROM NOW
    PUSH 	AX
	    MOV 	AX, 	TASKS_DATA_SEGMENT
	    MOV 	DS, 	AX
   	POP 	AX
    
    ; CALCULATING THE TASK CONTROL LOCATION
    PUSH    AX
    DEC     AX
    XOR     BX, BX
    MOV     BL, SIZE_TASK_INFO
    MUL     BL
    MOV     BX, AX
    POP     AX
	; BX = ASSIGNING TASK CONTROL OFFSET VALUE
    
    ; REGISTERING PERIOD AND DEADLINE
    MOV 	WORD PTR DS:[BX+OFFSET_TASK_PERIOD_POSITION], DX
    ADD 	DX, WORD PTR DS:GLOBAL_CLOCK                           
    MOV 	WORD PTR DS:[BX+OFFSET_TASK_DEADLINE_POSITION], DX
    
    ; REGISTERING SS AND SP
    MOV 	WORD PTR DS:[BX+OFFSET_TASK_SS_POSITION], ES
    MOV 	WORD PTR DS:[BX+OFFSET_TASK_SP_POSITION], SI
    ; ES:SI WERE STORED IN TASK CONTROL INFORMATION, FREE FROM NOW
    
    ; ENQUEUE TO THE READY QUEUE
    PUSH    AX
	    MOV     DX, AX
	    MOV     AX, TASKS_DATA_SEGMENT
	    MOV     ES, AX
	    MOV     AX, OFFSET READY_QUEUE
	    MOV     SI, AX
	    CALL    FAR PTR ENQUEUE
    POP     AX
    
    
    INC 	AX
    MOV     BYTE PTR DS:NEXT_AVAILABLE_ID, AL
    
    SUCCESS_REGISTERED:
    MOV     DX, TRUE
    JMP     REGISTER_TASK_QUIT
    
    FAIL_TO_REGISTER:
    MOV     DX, FALSE
    
    REGISTER_TASK_QUIT:

    ; LEAVING CRITICAL SECTION
    STI

    POP 	SI
    POP 	ES
    POP 	DI
    POP     DS
    POP     CX
    POP     BX
    POP     AX
    RET
REGISTER_TASK   ENDP

;=====================================================================
;	FUNCTION NAME: 	SCHEDULER
;	INPUTS: 		-
;	OUTPUTS:		-
;	DESCRIPTION:	PICK THE EALIEST DEADLINE TASK TO EXECUTE!
; 					**** THIS IS AN ISR FUNCTION!!!! SHOULD BE ASSIGNED TO 1 TIMER_ISR
;					
;=====================================================================
SCHEDULER   PROC FAR
    PUSH    AX
    PUSH    BX
    PUSH    CX
    PUSH    DX
    PUSH    BP
    PUSH    SI
    PUSH    DI
    PUSH    DS
    PUSH    ES        
    
    ; ENTER CRITICAL SECTION, PREVENTING SCHEDULER TO COME IN WHILE PERFORMING THE CONTEXT SWITCHING...
    CLI

    MOV     AX, TASKS_DATA_SEGMENT
    MOV     DS, AX 
    XOR     AX, AX
    MOV     AL, BYTE PTR DS:RUNNING_TASK
    
    ; AX = CURRENT_RUNNING_TASK_ID
            
    PUSH    AX
	    MOV     BX, TASKS_DATA_SEGMENT
	    MOV     ES, BX
	    MOV     BX, OFFSET READY_QUEUE
	    MOV     SI, BX
	    CALL 	FAR PTR PEEK_QUEUE
	    MOV     BX, AX
    POP     AX
    
    ; BX = FIRST_READY_TASK_ID
    
    
    ; IF THE QUEUE IS EMTPY, QUIT... NOTHING TO SCHEDULE
    CMP     BX, 0000H
    JE      SCHEDULER_QUITING
   
    
    ; IF THE QUEUE IS NOT EMTPY AND THE CURRENT TASK IS 
    ;    UI MAIN LOGIC (ALWAYS LOWEST PRIORITY)...
    ; 		ALWAYS SWITCH...
    ; OR THE ONE IN THE QUEUE IS UI MAIN LOGIC, 
    ; 	JUST IGNORE...
    CMP     AX, CONST_MAIN_LOGIC_ID
    JE      TASK_SWITCHING
    CMP     BX, CONST_MAIN_LOGIC_ID
    JE      NO_TASK_SWITCHING
    
    ; COMPARE THE DEADLINE OF CURRENT RUNNING TASK AND 
    ;    THE FIRST IN THE QUEUE
    PUSH    BX
	    CALL 	FAR PTR  GET_TASK_DEADLINE
	    MOV     CX, BX
    POP     BX 
    
    ; CX = CURRENT_RUNNING_TASK_DEADLINE
    
    PUSH    AX
	    MOV     AX, BX
	    CALL 	FAR PTR GET_TASK_DEADLINE
	    MOV     DX, BX
    POP     AX
    
    ; DX = FIRST_READY_TASK_DEADLINE
    
    ; SWITCH ONLY IF THE DEADLINE IS EARLIER
    CMP 	CX, DX
    JBE  	NO_TASK_SWITCHING
    
    TASK_SWITCHING:
        
        ; SWAP THE SS AND SP
        
        PUSH AX
	        DEC AX
	        MOV BL, SIZE_TASK_INFO
	        MUL BL
	        MOV BX, AX
        POP AX
                                                         
        MOV WORD PTR ES:[BX+OFFSET_TASK_SS_POSITION], SS        
        MOV WORD PTR ES:[BX+OFFSET_TASK_SP_POSITION], SP 
        
        MOV DX, AX
        CALL FAR PTR ENQUEUE
        
    LOAD_THE_NEXT_TASK:
        CALL FAR PTR DEQUEUE
        
        ; AX = FIRST TASK ID IN QUEUE AFTER CALLING DEQUEUE        
        MOV BYTE PTR DS:RUNNING_TASK, AL
        DEC AX
        MOV BL, SIZE_TASK_INFO
        MUL BL
        MOV BX, AX
                                                         
        MOV SS, WORD PTR ES:[BX+OFFSET_TASK_SS_POSITION]
        MOV SP, WORD PTR ES:[BX+OFFSET_TASK_SP_POSITION]

    NO_TASK_SWITCHING:
    
    
    SCHEDULER_QUITING: 
    
    ; LEAVE CRITICAL SECTION
    STI
    
    POP     ES
    POP     DS
    POP     DI
    POP     SI
    POP     BP
    POP     DX
    POP     CX
    POP     BX
    POP     AX
    IRET  
SCHEDULER   ENDP


;=====================================================================
;	FUNCTION NAME: 	SURRENDER_CONTROL
;	INPUTS: 		-
;	OUTPUTS:		-
;	AFFECTED REGS 	AX, BX (*******VALUE WILL BE CHANGED)
;	DESCRIPTION:	THIS FUNCTION CAN BE CALLED BY THE TASKS,
;						WHEN THEY FINISH TASK IN CURRENT CYCLE
;						AND WANT TO SLEEP UNTIL THE NEXT CYCLE.
;
;	IDEA:			PRETENT THE STACK IS CALLED BY INTERRUPT,
;					SO THAT THE SCHEDULER WILL NOT HAVE PROBLEM TO 
;					RESUME THESE TASKS..
;					
;=====================================================================
SURRENDER_CONTROL PROC FAR
    ; THE SCHEDULER IS TRIGGERED BY INTERRUPT, AND THIS SURRENDER_CONTROL
    ; IS CALLED BY THE TASK TO SURRENDER THE CONTROL BACK TO THE SYSTEM...
    
    ; WHEN CONTEXT SWICTHING, THE CONSISTENCY OF THE STACK SITUATION NEED TO BE THE SAME..
    
    ; WE ARE TRYING TO:
    ; TRYING TO SIMULATE THAT THIS IS CALLED BY AN INTERRUPT
    ; INTERRUPT CALL WILL PUSH FLAGS, CS, IP
    ; BUT NORMAL FAR CALL WILL ONLY PUSH CS, IP
    ; **** THEREFORE, REFORMATING THE STACK IS REQUIRED.
    
    POP     AX  ; RECORD THE IP
    POP     BX  ; RECORD THE CS
    PUSHF   ; PUSH FLAGS
    PUSH    BX
    PUSH    AX
    
    ; CONTEXT SWITCHING...
    PUSH    AX
    PUSH    BX
    PUSH    CX
    PUSH    DX
    PUSH    BP
    PUSH    SI
    PUSH    DI
    PUSH    DS
    PUSH    ES        
    
    ; ENTER CRITICAL SECTION
    CLI
    
    
    ; READ THE CURRENT RUNNING TASK'S ID...
    MOV     AX, TASKS_DATA_SEGMENT
    MOV     ES, AX 
    XOR     AX, AX
    MOV     AL, BYTE PTR ES:RUNNING_TASK
    
    ; BACKUP THE SS AND SP LOCATION    
    PUSH AX
	    DEC AX
	    MOV BL, SIZE_TASK_INFO
	    MUL BL
	    MOV BX, AX
    POP AX
    ; BX = OFFSET OF THE TASK CONTROL INFORMATION
    ; AX = CURRENT TASK'S ID
                                                     
    MOV WORD PTR ES:[BX+OFFSET_TASK_SS_POSITION], SS        
    MOV WORD PTR ES:[BX+OFFSET_TASK_SP_POSITION], SP
    
    
    ; PUT THE CURRENT TASK INTO THE WAITING QUEUE
    ; PARAMETERS NEED FOR ENQUEUE FUNCTION
    
    ; ES  ----->   QUEUE_SEGMENT
    ; SI  ----->   QUEUE_OFFSET (FIRST LOCATION)
    ; DX  ----->   TASK ID     
    MOV SI, OFFSET WAITING_QUEUE
    MOV DX, AX
    CALL FAR PTR ENQUEUE    
    
    ; FETCH THE NEXT TASK TO RUN
    ; PARAMETERS NEED FOR ENQUEUE FUNCTION
    
    ; ================= INPUTS ===================                        
    ; ES  ----->   QUEUE_SEGMENT
    ; SI  ----->   QUEUE_OFFSET (FIRST LOCATION) 
    ;
    ; ================= OUTPUTS ==================
    ; AX  ----->   OUTPUT INDEX
    MOV SI, OFFSET READY_QUEUE 
    ;***********************
    CALL FAR PTR DEQUEUE
    ;***********************
    
    MOV     BYTE PTR ES:RUNNING_TASK, AL
    
    
    ; CONTEXT SWITCHING BY RESTORING THE SS AND SP OF THE NEW TASK
    DEC AX
    MOV BL, SIZE_TASK_INFO
    MUL BL
    MOV BX, AX
    ; BX = OFFSET OF THE TASK CONTROL INFORMATION
    ; AX = NEXT TASK'S ID
                                                     
    MOV SS, WORD PTR ES:[BX+OFFSET_TASK_SS_POSITION]        
    MOV SP, WORD PTR ES:[BX+OFFSET_TASK_SP_POSITION]

    ; LEAVE CRITICAL SECTION
    STI
    
    SURRENDER_CONTROL_QUITING:
    POP     ES
    POP     DS
    POP     DI
    POP     SI
    POP     BP
    POP     DX
    POP     CX
    POP     BX
    POP     AX
    IRET
SURRENDER_CONTROL ENDP 

;=====================================================================
;	FUNCTION NAME: 	CLOCK_AND_TRACKER
;	INPUTS: 		-
;	OUTPUTS:		-
;	DESCRIPTION:	1.) KEEP TRACK THE GLOBAL TIMER.
;					2.) WAKE UP THE TASKS IN THE WAITING QUEUE IF ITS
;							DEADLINE IS REACHED. (START OF NEXT CYCLE)
;
; 					**** THIS IS AN ISR FUNCTION!!!! SHOULD BE ASSIGNED TO 1 TIMER_ISR
;					**** CLOCK OVERFLOW IS PREVENTED BY LIMITTING THE MAX,
;							POSSIBLE VALUE OF GLOBAL CLOCK AND TASKS' DEADLINE.
;					
;=====================================================================
CLOCK_AND_TRACKER PROC FAR
    PUSH    AX
    PUSH    BX
    PUSH    CX
    PUSH    DX
    PUSH    ES
    PUSH    SI        
    
; ENTER CRITICAL SECTION
    CLI
    
; STEP 1: UPDATE GLOBAL CLOCK
;           AND ADJUST THE CLOCK VALUE SUCH THAT OVERFLOW WILL NEVER HAPPEN

    ; READ AND UPDATE THE CURRENT CLOCK VALUE...
        MOV     AX, TASKS_DATA_SEGMENT
        MOV     ES, AX 
        MOV     AX, WORD PTR ES:GLOBAL_CLOCK
        INC     AX
        CMP     AX, GLOBAL_CLOCK_OVERFLOW_PREVENTION_THESHOLD
        JB      CLOCK_OVERFLOW_PREVENTION_NOT_REQUIRED
                       
        CLOCK_OVERFLOW_PREVENTION_REQUIRED:
    ; REDUCE THE GLOBAL CLOCK
        SUB     AX, GLOBAL_CLOCK_REDUCTION
        MOV     WORD PTR ES:GLOBAL_CLOCK, AX 
        
    ; ALL THE TASK'S DEADLINE ARE ALSO NEEDED TO BE REDUCED, EXCEPT THE TASK 1, MAIN UI LOGIC HAVE NO DEADLINE
        XOR     AX, AX
        MOV     AL, BYTE PTR ES:NEXT_AVAILABLE_ID 
        DEC     AL
        MOV     BX, GLOBAL_CLOCK_REDUCTION          
        ; AX = # OF TASK REGISTERED
        REDUCE_ALL_DEADLINE:
            CMP 	AL, CONST_MAIN_LOGIC_ID
            JE  	CHECK_THE_WAITING_TASK
	            CALL 	FAR PTR DEADLINE_REDUCTION 
	            DEC 	AL
            	JMP     REDUCE_ALL_DEADLINE
        
        CLOCK_OVERFLOW_PREVENTION_NOT_REQUIRED:
        MOV     WORD PTR ES:GLOBAL_CLOCK, AX                   
                               
    
; STEP 2: CHECK WHETHER A WAITING TASKS SHOULD BE WAKE UP 
;           AND INSERT INTO THE READY QUEUE
    CHECK_THE_WAITING_TASK:  
        MOV     SI, OFFSET WAITING_QUEUE
        XOR 	AX, AX
        MOV     AL, BYTE PTR ES:[SI]
        CMP     AL, 0
        JE      NO_MORE_TASK_IN_THE_WAITING_QUEUE
        
        DEC     AX
        MOV     BL, SIZE_TASK_INFO
        MUL     BL
        MOV     BX, AX
        MOV     CX, WORD PTR ES:[BX+OFFSET_TASK_DEADLINE_POSITION]
        CMP     CX, WORD PTR ES:GLOBAL_CLOCK
        JA      NO_MORE_TASK_IN_THE_WAITING_QUEUE
        
        ; IF THE DEADLINE IS SMALLER/EQUAL TO THE CLOCK NOW,
        ;     NEED TO BE WAKEN UP AND INSERT INTO THE READY QUEUE..
        ; **** THE DEADLINE VALUE NEED TO CHANGE TO THE NEXT DEADLINE AS WELL...
        
        ADD_UNTIL_LARGE_THAN_GLOBAL_CLOCK:
            ADD     CX, WORD PTR ES:[BX+OFFSET_TASK_PERIOD_POSITION]
            CMP     CX, WORD PTR ES:GLOBAL_CLOCK
            JB      ADD_UNTIL_LARGE_THAN_GLOBAL_CLOCK
        MOV     WORD PTR ES:[BX+OFFSET_TASK_DEADLINE_POSITION], CX
                                                                                        
        MOV     SI, OFFSET WAITING_QUEUE
        CALL FAR PTR DEQUEUE
        MOV     SI, OFFSET READY_QUEUE
        MOV     DX, AX
        CALL FAR PTR ENQUEUE
        
        JMP     CHECK_THE_WAITING_TASK 
        
    NO_MORE_TASK_IN_THE_WAITING_QUEUE:
    
    GLOBAL_CLOCK_QUITING:
    ; LEAVE CRITICAL SECTION
    STI
    
    POP     SI
    POP     ES
    POP     DX
    POP     CX
    POP     BX
    POP     AX
    IRET
CLOCK_AND_TRACKER ENDP       

;=====================================================================
;	FUNCTION NAME: 	DEADLINE_REDUCTION
;	INPUTS: 		AX = TASK ID
; 					BX = REDUCE BY THIS VALUE  
;	OUTPUTS:		-
;	DESCRIPTION:	REDUCE THE DEADLINE OF THE TASK BY THE TASK ID AND REDUCTION VALUE.
;=====================================================================
DEADLINE_REDUCTION PROC FAR
    PUSH    AX
    PUSH    BX
    PUSH    ES
    
    PUSH    AX
    MOV     AX, TASKS_DATA_SEGMENT
    MOV     ES, AX
    POP     AX

    DEC     AX
    MOV     BX, SIZE_TASK_INFO
    MUL     BL
    MOV     BX, AX
    
    MOV     AX, WORD PTR ES:[BX+OFFSET_TASK_DEADLINE_POSITION]
    SUB     AX, GLOBAL_CLOCK_REDUCTION
    MOV     WORD PTR ES:[BX+OFFSET_TASK_DEADLINE_POSITION], AX
    
    POP     ES
    POP     BX
    POP     AX
    RET
DEADLINE_REDUCTION ENDP  

TASK_FUNCTIONS		ENDS

End